//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using CommunityServer.Components;

namespace CommunityServer.Configuration
{
    /// <summary>
    /// Configurable IJob descritpion
    /// </summary>
    [Serializable]
    [XmlRoot("job")]
    public class Job : IDisposable
    {
        public event EventHandler PreJob;
        public event EventHandler PostJob;

        private void OnPreJob()
        {
            try
            {
                if(PreJob != null)
                    PreJob(this,EventArgs.Empty);
            }
            catch(Exception ex)
            {
                EventLogs.Warn("PreJob listener failed", "Jobs", 807,ex, CSContext.Current.SettingsID);
            }
        }

        private void OnPostJob()
        {
            try
            {
                if(PostJob != null)
                    PostJob(this,EventArgs.Empty);
            }
            catch(Exception ex)
            {
                EventLogs.Warn("Post listener failed", "Jobs", 807,ex, CSContext.Current.SettingsID);
            }
        }

        public Job(Type ijob, XmlNode node)
        {
            _node = node;

            _jobType = ijob;

            XmlAttribute att =  node.Attributes["enabled"];
            if(att != null)
               this._enabled = bool.Parse(att.Value);

            att = node.Attributes["enableShutDown"];
            if(att != null)
               this._enableShutDown = bool.Parse(att.Value);

            att = node.Attributes["name"];
            if(att != null)
                this._name = att.Value;

            att = node.Attributes["seconds"];
            if(att != null)
            {
                _seconds = Int32.Parse(att.Value);
            }

            att = node.Attributes["firstRun"];
            if(att != null)
            {
                _firstRun = Int32.Parse(att.Value);
            }

            att = node.Attributes["minutes"];
            if(att != null)
            {
                try
                {
                    this._minutes = Int32.Parse(att.Value);
                }
                catch
                {
                    this._minutes = 15;
                }
            }

            att = node.Attributes["singleThread"];
            if(att != null && !Globals.IsNullorEmpty(att.Value) && string.Compare(att.Value,"false", false) == 0)
                _singleThread = false;
        }

        

        #region Private Members
        
        private IJob _ijob;
        private bool _enabled = true;
        private Type _jobType;
        private string _name;
        private bool _enableShutDown = false;
        private int _minutes = 15;
        private Timer _timer = null;
        private bool disposed = false;
        private XmlNode _node = null;
        private bool _singleThread = true;
        private DateTime _lastStart;
        private DateTime _lastSucess;
        private DateTime _lastEnd;
        private bool _isRunning;
        private int _seconds = -1;
        private int _firstRun = -1;

        protected int Interval
        {
            get
            {
                if(_firstRun > 0)
                    return _firstRun * 1000;

                if(_seconds > 0)
                    return _seconds * 1000;

                return Minutes * 60000;
            }
        }

        #endregion


        /// <summary>
        /// Creates the timer and sets the callback if it is enabled
        /// </summary>
        public void InitializeTimer()
        {
            if(_timer == null && Enabled)
            {
                _timer = new Timer(new TimerCallback(timer_Callback), null,Interval, Interval);
            }
        }

        /// <summary>
        /// Internal call back which is responsible for firing IJob.Execute()
        /// </summary>
        /// <param name="state"></param>
        private void timer_Callback(object state)
        {
//            Guid id = (Guid)state;
//            if(id != Jobs.Instance().CurrentID)
//            {
//                this.Dispose();
//                return;
//            }



            if(!Enabled)
                return;

           _timer.Change( Timeout.Infinite, Timeout.Infinite );

            _firstRun = -1;

            

            ExecuteJob();

            

            if(Enabled)
                _timer.Change( Interval, Interval);
            else
                this.Dispose();

        }

        public void ExecuteJob()
        {
            OnPreJob();

            _isRunning = true;
            IJob ijob = this.CreateJobInstance();
            if(ijob != null)
            {
                _lastStart = DateTime.Now;
                try
                {
                    ijob.Execute(this._node);
                  _lastEnd = _lastSucess = DateTime.Now;
                     
                }
                catch(Exception)
                {
                    this._enabled = !this.EnableShutDown;
                    _lastEnd = DateTime.Now;
                }
            }
            _isRunning = false;

            OnPostJob();
        }

        #region Public Properities
     
        public bool IsRunning
        {
            get{return _isRunning;}
        }

        public DateTime LastStarted
        {
            get{ return _lastStart;}
        }

        public DateTime LastEnd
        {
            get{ return _lastEnd;}
        }

        public DateTime LastSuccess
        {
            get{ return _lastSucess;}
        }
  
        public bool SingleThreaded
        {
            get{ return _singleThread;}
        }

        /// <summary>
        /// Named type of class which implements IJob
        /// </summary>
        public Type JobType
        {
            get {  return this._jobType; }
           
        }

        public int Minutes
        {
            get{return _minutes;}
            set{_minutes = value;}
        }

        
        /// <summary>
        /// On an exception, should this job be deactivated
        /// </summary>
        public bool EnableShutDown
        {
            get {  return this._enableShutDown; }
        }

        
        /// <summary>
        /// Name of Job
        /// </summary>
        public string Name
        {
            get {  return this._name; }
        }
        
        /// <summary>
        /// Is this job enabled
        /// </summary>
        public bool Enabled
        {
            get {  return this._enabled; }
        }


        /// <summary>
        /// Attempts to create an instance of the IJob. If the type
        /// can not be created, this Job will be disabled.
        /// </summary>
        /// <returns></returns>
        public IJob CreateJobInstance()
        {
            if(Enabled)
            {
                if(_ijob == null)
                {
                    
                    if(_jobType != null)
                    {
                        _ijob = Activator.CreateInstance(_jobType) as IJob;
                    }
                    _enabled = (_ijob != null);

                    if(!_enabled)
                        this.Dispose();
                }
            }
            return _ijob;
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
           if(_timer!= null && !disposed)
           {
               lock(this)
               {
                   _timer.Dispose();
                   _timer = null;
                   disposed = true;
               }
           }
        }



        #endregion
    }
}
